package peace.transport.xmlrpc
{
	import peace.transport.xmlrpc.type.XmlrpcArray;
	import peace.transport.xmlrpc.type.XmlrpcStruct;
	
	import mx.utils.Base64Decoder;
	import mx.utils.Base64Encoder;
	

[ExcludeClass]
public class Xmlrpc
{
	/**
	 * формат кодирования даты 
	 */	
	public static const DATE_TIME_TYPE:String = "dateTime";
	public static const DATE_TYPE:String = "date";
	/**
	 * Форматы данных xml-rpc 
	 * 
	 */	
	public static const INT:String = "i4";
	public static const INT1:String = "int";
	public static const ARRAY:String = "array";
	public static const BASE64:String = "base64";
	public static const BOOLEAN:String = "boolean";
	public static const DATE:String = "dateTime.iso8601";
	public static const DOUBLE:String = "double";
	public static const STRING:String = "string";
	public static const STRUCT:String = "struct";
	public static const NIL:String = "nil";
	
	public function Xmlrpc()
	{
	}
	
	/**
	* Декодируем данные определенного типа из xml-rpc > AS3
	*/	
	public static function decodeValue (xml:XML):*
	{
		if (! xml) return null;
		
		switch (xml.localName())
		{
			case ARRAY:
			{
				return XmlrpcArray.parse(xml);
			}
			case STRUCT:
			{
				return XmlrpcStruct.parse(xml);
			}
			case DATE:
			{
				return decodeDate(xml.*[0]);
			}
			case INT:
			case INT1:
			case DOUBLE:
			{
				return Number(xml.*[0]);
			}
			case BOOLEAN:
			{
				var s:String = String(xml.*[0]).toLowerCase();
				return s=="1" || s=="true";
			}
			case NIL:
			{
				return null;
			}
			case STRING:
			{
				return String(xml.*[0]);
			}
			case BASE64:
			{
				var dec:Base64Decoder = new Base64Decoder();
				dec.decode(String(xml.*[0]));
				return dec.toByteArray().toString();
			}
			default:
			{
				return String(xml.*[0]);
			}
		}
	}
	
	
    /**
     * Взял из mx.rpc.xml.SimpleXMLEncoder, спасибо :) 
     * @param rawDate
     * @param dateType
     * @return 
     * 
     */	
    static private function encodeDate(rawDate:Date, dateType:String=DATE_TIME_TYPE):String
    {
        var s:String = new String();
        var n:Number;

        if (dateType == "dateTime" || dateType == "date")
        {
            s = s.concat(rawDate.getUTCFullYear(), "-");

            n = rawDate.getUTCMonth()+1;
            if (n < 10) s = s.concat("0");
            s = s.concat(n, "-");

            n = rawDate.getUTCDate();
            if (n < 10) s = s.concat("0");
            s = s.concat(n);
        }

        if (dateType == "dateTime")
        {
            s = s.concat("T");
        }

        if (dateType == "dateTime" || dateType == "time")
        {
            n = rawDate.getUTCHours();
            if (n < 10) s = s.concat("0");
            s = s.concat(n, ":");

            n = rawDate.getUTCMinutes();
            if (n < 10) s = s.concat("0");
            s = s.concat(n, ":");

            n = rawDate.getUTCSeconds();
            if (n < 10) s = s.concat("0");
            s = s.concat(n, ".");

            n = rawDate.getUTCMilliseconds();
            if (n < 10) s = s.concat("00");
            else if (n < 100) s = s.concat("0");
            s = s.concat(n);
        }

        s = s.concat("Z");

        return s;
    }
	// TODO: написать
	private static function decodeDate (str:String):Date
	{
		var d:Date = new Date();
		return d;
	}
	
	/**
	 * Переводим AS-данные в xml-rpc
	 * 
	 * TODO: обработать BASE64
	 *  
	 * @param v
	 * @return 
	 * 
	 */	
	public static function encodeValue (v:*):XML
	{
		if (! isNaN(v))
		{
			var n:Number = Number (v);
			if (n % 1 == 0)
			{
				return <{INT}>{n}</{INT}>
			}
			else
			{
				return <{DOUBLE}>{n}</{DOUBLE}>
			}
		}
		
		if (v is Array)
		{
			if (v is XmlrpcArray)
			{
				return XmlrpcArray(v).xml;
			}
			return (new XmlrpcArray (v as Array)).xml;
		}
		
		if (v is Boolean)
		{
			return <{BOOLEAN}>{Number(v)}</{BOOLEAN}>
		}
		
		if (v is Date)
		{
			return <{DATE}>{encodeDate(v as Date)}</{DATE}>
		}
		
		if (v is String)
		{
			var s:String = String(v).toLowerCase();
			if (s=="true" || s=="false")
			{
				return <{BOOLEAN}>{Number(s=="true")}</{BOOLEAN}>
			}
			return <{STRING}>{String(v)}</{STRING}>
		}
		
		if (v is Object)
		{
			if (v is XmlrpcStruct)
			{
				return XmlrpcStruct(v).xml;
			}
			return (new XmlrpcStruct (v)).xml;
		}
		
		return <nil/>;
	}

	public static function encodeToBase64 (str:String):XML
	{
		var enc:Base64Encoder = new Base64Encoder();
		enc.encodeUTFBytes(str);
		return <{BASE64}>{enc.toString()}</{BASE64}>
	}
	

}
}